package com.leetcode;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class Leetcode037 {
    /*
    solution 1:
    //9 行
    private boolean[][] line = new boolean[9][9];
    //9列
    private boolean[][] column = new boolean[9][9];
    // 3 x 3 个小9方格,这里也可以改成 9x9，参照leetcode036
    private boolean[][][] block = new boolean[3][3][9];
    //作为及时结束的标志，防止多个递归分支将board的值再次被改变
    private boolean valid = false;
    //存储未赋值的方格
    private List<int[]> spaces = new ArrayList<int[]>();

    public void solveSudoku(char[][] board) {
        for (int i = 0; i < 9; ++i) {
            for (int j = 0; j < 9; ++j) {
                //将所有未赋值的方块的位置记录下来
                if (board[i][j] == '.') {
                    spaces.add(new int[]{i, j});
                } else {
                    //board[i][j] - '0' 字符转成数字
                    //减1 是因为数独数值范围是1~9，而数组从0开始计算
                    int digit = board[i][j] - '0' - 1;
                    //按照数独的逻辑，每个数字只会出现一次，因此我们可以不考虑数字重复的问题
                    //将已赋值的方块，按照横，竖，方格做上标记
                    line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = true;
                }
            }
        }

        dfs(board, 0);
    }

    public void dfs(char[][] board, int pos) {
        if (pos == spaces.size()) {
            valid = true;
            return;
        }

        int[] space = spaces.get(pos);
        int i = space[0], j = space[1];
        //每个空着的方格，都要从1~9 试一遍
        for (int digit = 0; digit < 9 && !valid; ++digit) {
            //判断横线，竖线，小9方格内是否存在重复的数字，如果重复则跳过
            //如果1~9都不满足，就相当于递归结束
            if (!line[i][digit] && !column[j][digit] && !block[i / 3][j / 3][digit]) {
                line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = true;
                //int to char 逆向操作，记得加上1
                board[i][j] = (char) (digit + '0' + 1);
                //space按顺序读取，如果前面的满足要求，那么就会一直递归下去。
                dfs(board, pos + 1);
                //假设数独是唯一解，如果之前的数字不满足要求，就将标志位清除，
                //board[i][j]的值没必要清除，因为后面再赋值就覆盖了原来的值,关键还是标志位不要错
                line[i][digit] = column[j][digit] = block[i / 3][j / 3][digit] = false;
            }
        }
    }
*/
    //solution 2: 思路同solution1，写法上更加简洁
    public void solveSudoku(char[][] board) {
        if (board == null || board.length == 0) {
            return;
        }

        solve(board);
    }

    private boolean solve(char[][] board) {
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[0].length; j++) {
                if (board[i][j] == '.') {
                    for (char ch = '1'; ch <= '9'; ch++) {
                        if (isValid(board, i, j, ch)) {
                            board[i][j] = ch;
                            if (solve(board)) {
                                return true;
                            } else {
                                board[i][j] = '.';
                            }
                        }
                    }
                    return false;
                }
            }
        }
        return true;
    }

    private boolean isValid(char[][] board, int row, int col, char ch) {
        for (int i = 0; i < 9; i++) {
            // check row
            char temp1 = board[row][i];
            if (temp1 != '.' && temp1 == ch) {
                return false;
            }

            //check column
            char temp2 = board[i][col];
            if (temp2 != '.' && temp2 == ch) {
                return false;
            }

            //没搞明白
            char temp3 = board [3 * (row / 3) + i/ 3][ 3* (col / 3)+ i% 3];
            if (temp3 != '.' && temp3 == ch) {
                return false;
            }
        }

        return true;
    }


    public static void main(String[] args) {
        Leetcode037 leetcode036 = new Leetcode037();
        char[][] board = new char[][]{{'5', '3', '.', '.', '7', '.', '.', '.', '.'}
                , {'6', '.', '.', '1', '9', '5', '.', '.', '.'}
                , {'.', '9', '8', '.', '.', '.', '.', '6', '.'}
                , {'8', '.', '.', '.', '6', '.', '.', '.', '3'}
                , {'4', '.', '.', '8', '.', '3', '.', '.', '1'}
                , {'7', '.', '.', '.', '2', '.', '.', '.', '6'}
                , {'.', '6', '.', '.', '.', '.', '2', '8', '.'}
                , {'.', '.', '.', '4', '1', '9', '.', '.', '5'}
                , {'.', '.', '.', '.', '8', '.', '.', '7', '9'}};

        leetcode036.solveSudoku(board);
        for (int i = 0; i < board.length; i++) {
            for (int j = 0; j < board[i].length; j++) {
                System.out.print(board[i][j] + ",");
            }
            System.out.println();
        }
    }
}
